package de.hauschild.orbitvm.controller.impl;

import java.awt.Color;

import javax.swing.JLabel;

import org.apache.commons.math.geometry.Vector3D;

import de.hauschild.orbitvm.visualization.VMFrame;
import de.hauschild.orbitvm.vm.DoublePoint;
import de.hauschild.orbitvm.vm.VM;

/**
 * The {@link de.hauschild.orbitvm.controller.Controller Controller} for the Hohmann transfer from one circular orbit to
 * another.
 * 
 * @since 1.0.0
 * @author Klaus Hauschild
 */
public class HohmannController extends AbstractController {

  private static final int OUTPUT_INDEX__TARGET_ORBIT = 4;
  private static final double G = 6.67428E-11;
  private static final double MY = 6E24 * G;

  public static DoublePoint getStartImpulse(final VM vm, final double tagretOrbitRadius) {
    // velocity
    final Double rp = vm.getDistanceToEarth();
    final Double ra = tagretOrbitRadius;
    final Double vpKreis = Math.sqrt(MY / rp);
    final Double a = (rp + ra) / 2.0;
    final Double vp = Math.sqrt((MY / a) * (ra / rp));
    final Double vStart = vpKreis - vp;
    // impulse
    final double x = vm.getSatellitePosition().getX();
    final double y = vm.getSatellitePosition().getY();
    final double nx = 1.0;
    final double ny = -(x / y);
    final Vector3D n = new Vector3D(nx, ny, 0.0).normalize();
    final DoublePoint startImpulse = new DoublePoint(vStart * n.getX(), vStart * n.getY());
    return startImpulse;
  }

  public static DoublePoint getStopImpulse(final VM vm, final double targetOrbitRadius) {
    // velocity
    final Double rp = vm.getDistanceToEarth();
    final Double ra = targetOrbitRadius;
    final Double vaKreis = Math.sqrt(MY / ra);
    final Double a = (rp + ra) / 2.0;
    final Double va = Math.sqrt((MY / a) * (rp / ra));
    final Double vStop = vaKreis - va;
    // impulse
    final double x = vm.getSatellitePosition().getX();
    final double y = vm.getSatellitePosition().getY();
    final double nx = 1.0;
    final double ny = -(x / y);
    final Vector3D n = new Vector3D(nx, ny, 0.0).normalize();
    final DoublePoint stopImpulse = new DoublePoint(vStop * n.getX(), vStop * n.getY());
    return stopImpulse;
  }

  private final JLabel targetRadiusLabel;
  private final JLabel distanceToTargetOrbitLabel;

  private boolean stoped;

  private DoublePoint stopImpulse;

  /**
   * Instantiates a new {@link HohmannController}.
   * 
   * @param theVM
   *          the virtual machine
   * @param theVMFrame
   *          the virtual machine visualization frame
   */
  public HohmannController(final VM theVM, final VMFrame theVMFrame) {
    super(theVM, theVMFrame);
    targetRadiusLabel = vmFrame().createControllerLabel("Taget radius:");
    distanceToTargetOrbitLabel = vmFrame().createControllerLabel("distance to target orbit:");
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void afterIteration() {
    targetRadiusLabel.setText(String.format(DOUBLE_FORMAT_STRING, getTargetOrbitRadius()));
    final int targetRadius = (int) (getTargetOrbitRadius() / VMFrame.SIZE_FACTOR);
    if (vmFrame().getImageGraphics() != null) {
      vmFrame().getImageGraphics().setColor(Color.GREEN);
      vmFrame().getImageGraphics().drawOval(VMFrame.IMAGE_SIZE / 2 - targetRadius,
          VMFrame.IMAGE_SIZE / 2 - targetRadius, 2 * targetRadius, 2 * targetRadius);
    }
    vmFrame().repaint();

    final double distanceToTargetOrbit = Math.abs(getTargetOrbitRadius() - vm().getDistanceToEarth());
    distanceToTargetOrbitLabel.setForeground(Color.RED);
    if (distanceToTargetOrbit <= ONE_KILOMETER) {
      distanceToTargetOrbitLabel.setForeground(Color.GREEN);
    }
    distanceToTargetOrbitLabel.setText(String.format(DOUBLE_FORMAT_STRING, distanceToTargetOrbit));
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void beforeIteration() {
    if (Math.abs(getTargetOrbitRadius() - vm().getDistanceToEarth()) < 1.0 && !stoped) {
      vm().setVelocityImpulse(stopImpulse);
      stoped = true;
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void initialize() {
    stopImpulse = getStopImpulse(vm(), getTargetOrbitRadius());
    // start impulse
    vm().setVelocityImpulse(HohmannController.getStartImpulse(vm(), getTargetOrbitRadius()));
  }

  /**
   * Gets the target orbit radius.
   * 
   * @return the target orbit radius
   */
  private Double getTargetOrbitRadius() {
    return vm().getOutput()[OUTPUT_INDEX__TARGET_ORBIT];
  }

}
